#define LOG_NDEBUG 0
#define LOG_TAG "NetAPTM"

#include "ap/NetAPTM.h"

#include <ipcp/ipcp.h>
#include <ipcp/packet.h>
#include "VHalLog.h"
#include <transfer/SafeQueue.h>
#include <transfer/SignalStorage.h>
#include <transfer/utils.h>

#include <set>
#include <thread>

#include "common.h"
#include "ap/APTM_signal_id.h"
#include "converAPTMValue.h"


#define INVALID_SERVICE_ID 0x0

#define SEND_RETRY_MAX_COUNT 3
#define PACKET_HEADER_OFFSET IPCP_LEAST_LEN
#define RETRY_TRANSPORT_CONF_MAX_COUNT 5

namespace ecarx::transfer::aptm {

    static utils::ConcurrentQueue<ecarx::transfer::ipcp::IpcpValue> mIpcpQueue;
    static utils::BatchingConsumer<ecarx::transfer::ipcp::IpcpValue> mBatchingConsumer;

    static std::set<uint16_t> packageReceived;

    template<typename T, size_t N = sizeof(T)>

    std::vector<uint8_t> toBytes(T value) {
        std::vector<uint8_t> data;
        data.resize(N, 0);
        memcpy(data.data(), &value, N);
        return data;
    }

    class APTMConn::NetAPTM {
    public:
        NetAPTM() {
            mState = NetState::INIT;
            mBatchingConsumer.run(&mIpcpQueue,
                                  std::bind(&NetAPTM::readThread, this, std::placeholders::_1));
            mIPCPCallbacks = new ipcp_callbacks_t;
            mIPCPCallbacks->ipcp_received_cb = netReceived;
            mIPCPCallbacks->error_cb = error_cb;
            initTransport_conf();
            VHALOG.d("APTM init construct");
        }

        ~NetAPTM() {
            VHALOG.d("APTM release");
            mBatchingConsumer.requestStop();
            mIpcpQueue.deactivate();
            // We have to wait until consumer thread is fully stopped because it may
            // be in a state of running callback (onBatchHalEvent).
            mBatchingConsumer.waitStopped();
            close();
        }

        //打开链接
        bool open() {
            notifyNetState(NetState::Open);
            bool isTransportConfErr = false;
            int retryTransportConfCount = 0;
            ipcp_stack_err_t error_code = STACK_START_SUCCESS;
            do {
                for (const ipcp_transport_conf_t conf : mTransportConfigs) {
                    ValidOperationArray operIdsArray;
                    operIdsArray.len = 21;
                    operIdsArray.oper_ids = CPOper;
                    error_code = ipcp_setup_with_opids(conf, mIPCPCallbacks, operIdsArray.oper_ids,
                                                       operIdsArray.len);
                    if (error_code != 0) {
                        VHALOG.e("funtion: %s, APTM_setup_with_opids service id %d ,failed, errno=%d",
                              __FUNCTION__, conf.service_id, error_code);
                        if (error_code == STACK_FAILURE_CREATE_SOCKET ||
                            error_code == STACK_FAILURE_SET_VLAN ||
                            error_code == STACK_FAILURE_BIND_SOCKET ||
                            error_code == STACK_FAILURE_LISTEN_SOCKET ||
                            error_code == STACK_FAILURE_ACCEPT_SOCKET ||
                            error_code == STACK_FAILURE_CONNECT_SOCKET ||
                            error_code == STACK_FAILURE_CREATE_THREAD ||
                            error_code == STACK_FAILURE_PARM_INVALID) {
                            VHALOG.e("funtion: %s, APTM isTransportConfErr is true", __FUNCTION__);
                            isTransportConfErr = true;
                            break;
                        }
                    }
                }
                if (isTransportConfErr) {
                    ipcp_cleanup();
                    std::this_thread::sleep_for(std::chrono::milliseconds(500));
                    retryTransportConfCount++;
                    if (retryTransportConfCount > RETRY_TRANSPORT_CONF_MAX_COUNT) {
                        break;
                    }
                    VHALOG.e("funtion: %s, APTM_setup_with_opids() err[%d], cleanup and retry(%d/%d)",
                          __FUNCTION__, error_code, retryTransportConfCount,
                          RETRY_TRANSPORT_CONF_MAX_COUNT);
                }
            } while (isTransportConfErr);
            openState = error_code == STACK_START_SUCCESS;
            if (openState) {
                VHALOG.d("open APTM success");
                getAll();
            } else {
                VHALOG.e("open APTM failed");
            }
            return openState;
        }
        //关闭链接
        bool close() {
            notifyNetState(NetState::Close);
            ipcp_cleanup();
            return true;
        }
        //将数据写到网络上
        bool write(const Signal& signal) {
            ecarx::transfer::ipcp::IpcpValue ipValue;
            ipValue = ecarx::transfer::utils::tmSignalToIpcpValue(signal);
            ipcp_packet_t *packet =
                    build_ipcp_packet(ipValue.service_id, ipValue.oper_id, ipValue.oper_type,
                                      ipValue.payload_type, ipValue.payload, ipValue.payload_len);
            VHALOG.e("APTM send:service_id=[%d],oper_id=[%d],oper_type=[%d],payload_type=[%d],payload_len=[%ld]",
                  ipValue.service_id, ipValue.oper_id, ipValue.oper_type,
                  ipValue.payload_type, ipValue.payload_len);
            if (ipValue.payload_len > 0) {
                delete[](uint8_t*) ipValue.payload;  // release asn1 payload buffer, ipcp stack
                                                     // already copy buffer to itself.
            }
            if (packet != NULL) {
                ipcp_peer_t peer;
                memset(peer.peer_ip, 0, sizeof(peer.peer_ip));
                strcpy(peer.peer_ip, ipValue.peer_ip);  // service ip
                peer.peer_port = ipValue.peer_port;
                peer.sk_type = IPCP_TRANSPORT_UDP;
                ipcp_stack_err_t err = STACK_SEND_MSG_SUCCESS;
                if ((err = ipcp_send(peer, packet)) != STACK_SEND_MSG_SUCCESS) {
                    VHALOG.e("funtion: %d, APTM_send err : %d", signal.signalId, err);
                    return false;
                }
                return true;
            } else {
                VHALOG.e("funtion: %d, APTM build packet failed is not writeable", signal.signalId);
            }
            return false;
        }

        const std::optional<Signal> read(const int32_t signalId) const {
            return mAPTMStorage.get(signalId);
        }

        NetState getState() {
            return mState;
        }

        void notifyNetState(NetState state) {
            mState = state;
            if (mStateCallback != nullptr) {
                mStateCallback(state);
            }
        }

        void onSignalChanged(SignalChangeCallback &&callback) { mSignalChangeCallback = callback; }

        void registerStateHandler(NetStateCallback &&callback) { mStateCallback = callback; }

        //接受网络上的数据回调
        static void netReceived(ipcp_packet_t *packet, ipcp_peer_t peer) {
            std::size_t payload_len = packet->header.length - PACKET_HEADER_OFFSET;
            if (payload_len > 0) {
                ecarx::transfer::ipcp::IpcpValue value;
                value.service_id = packet->header.service_id;
                value.oper_id = packet->header.operation_id;
                value.oper_type = packet->header.op_type;
                value.payload_type = packet->header.payload_type;
                // service ip
                memset(value.peer_ip, 0, sizeof(value.peer_ip));
                strcpy(value.peer_ip, peer.peer_ip);
                // service port
                value.peer_port = peer.peer_port;
                // payload
                value.payload_len = payload_len;
                value.payload = (void *) new uint8_t[value.payload_len];
                memcpy(value.payload, packet->payload, value.payload_len);
                VHALOG.d("funtion: %s, APTM service id %d opid %d ", __FUNCTION__, packet->header.service_id,
                      packet->header.operation_id);
                mIpcpQueue.push(std::move(value));
            }
        }

        static void error_cb(ipcp_packet_t *ipcp_packet, ipcp_stack_err_t error) {
            VHALOG.d("funtion: %s, APTM service id %d opid %d ipcp stack error : %d", __FUNCTION__,
                  ipcp_packet->header.service_id, ipcp_packet->header.operation_id, error);
        }

    private:
        void initTransport_conf() {
            // 初始化APTM 通信 IPCP端口
            mTransportConfigs.emplace_back(
                    ipcp_transport_conf_t{.service_id = static_cast<uint16_t>(TM_serviceID),
                            .host_ip = AP_LOCAL_IP,
                            .host_port = AP_LOCAL_PORT,
                            .sk_type = IPCP_TRANSPORT_UDP,
                            .vlan = AP_VLAN_ID,
                            .priority = AP_VLAN_PRIORITY,
                            .eth = "eth1"});
        }

        void readThread(const ecarx::transfer::ipcp::IpcpValue &value) {
            // 从netReceive的data分析数据内容
            std::vector<int32_t> changed;
            if (value.service_id == TM_serviceID) {
                // 信号
                std::vector<ecarx::transfer::Signal> buffer = ecarx::transfer::utils::tmIpcpValueToSignal(value);
                mAPTMStorage.put(buffer, changed);
            }
            VHALOG.e("APTM read : service_id=[%d],opId=[%d],opType[%d],payLoadLen=[%ld]",
                  value.service_id, value.oper_id, value.oper_type, value.payload_len);
            for (int i = 0; i < value.payload_len; i++) {
                VHALOG.e("APTM data : data[%d]:%d", i, *((uint8_t *) (value.payload) + i));
            }
            if (!changed.empty()) {
                mSignalChangeCallback(changed);
            }
            delete[](uint8_t *) value.payload;
        }

        void getAll() {
            // TODO get all from CP(dim/hud)?
        }

    private:
        std::unique_ptr<std::thread> mReadThread;

        SignalChangeCallback mSignalChangeCallback;
        NetStateCallback mStateCallback;

        ipcp_callbacks_t *mIPCPCallbacks;

        std::vector<ipcp_transport_conf_t> mTransportConfigs;
        storage::SignalPool mAPTMStorage;
        NetState mState;
        bool openState = false;
    };

    APTMConn::APTMConn() : mImpl(new NetAPTM()) {}

    APTMConn::~APTMConn() {
        delete mImpl;
    }

    bool APTMConn::open() {
        return mImpl->open();
    }

    bool APTMConn::close() {
        return mImpl->close();
    }

    bool APTMConn::write(const Signal &signal) {
        return mImpl->write(signal);
    }

    const std::optional<Signal> APTMConn::read(const int32_t signalId) const {
        return mImpl->read(signalId);
    }

    NetState APTMConn::getState() {
        return mImpl->getState();
    }

    void APTMConn::onSignalChanged(SignalChangeCallback &&callback) {
        mImpl->onSignalChanged(std::move(callback));
    }

    void APTMConn::registerStateHandler(NetStateCallback &&callback) {
        mImpl->registerStateHandler(std::move(callback));
    }

}  // namespace ecarx::transfer::ipcp
